package com.sky;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.*;

/**
 *
 *
 * 这个 Loader 其实是一个 集合 ;  它把 接口的实现类 装入, 供其他调用;
 *
 * modifier : wushikai
 * date : 2021/8/6
 */
public final class MyServiceLoader<S>    implements Iterable<S>{

    private static final String PREFIX = "META-INF/services/";

    // The class or interface representing the service being loaded 表示正在加载的服务的类或接口
    private final Class<S> service;

    // The class loader used to locate, load, and instantiate providers  用于定位、加载和实例化提供程序的类装入器
    private final ClassLoader loader;

    // The access control context taken when the MyServiceLoader is created 创建MyServiceLoader时采用的访问控制上下文
    private final AccessControlContext acc;

    // Cached providers, in instantiation order
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

    // The current lazy-lookup iterator
    private MyServiceLoader.LazyIterator lookupIterator;

    /**
     * Clear this loader's provider cache so that all providers will be
     * reloaded.
     *
     * <p> After invoking this method, subsequent invocations of the {@link
     * #iterator() iterator} method will lazily look up and instantiate
     * providers from scratch, just as is done by a newly-created loader.
     *
     * <p> This method is intended for use in situations in which new providers
     * can be installed into a running Java virtual machine.
     *
     * 清除这个加载器的提供程序缓存，以便重新加载所有的提供程序。
     * 在调用此方法之后，迭代器方法的后续调用将从零开始惰性地查找和实例化提供程序，就像新创建的加载器所做的那样。
     * 此方法用于可以将新的提供者安装到运行中的Java虚拟机中的情况
     */
    public void reload() {
        providers.clear();   //为什么要清掉???    是不是 加载 业务类之前, 就已经加载了 必须的 初始类
        lookupIterator = new  MyServiceLoader.LazyIterator(service, loader);
    }

    private MyServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }



    private static void fail(Class<?> service, String msg, Throwable cause)throws ServiceConfigurationError    {
        throw new ServiceConfigurationError(service.getName() + ": " + msg,  cause);
    }

    private static void fail(Class<?> service, String msg)throws ServiceConfigurationError{
        throw new ServiceConfigurationError(service.getName() + ": " + msg);
    }

    private static void fail(Class<?> service, URL u, int line, String msg)throws ServiceConfigurationError{
        fail(service, u + ":" + line + ": " + msg);
    }


    /**
     * 解析 所属行的字符串是否是  java 类
     * */
    private int parseLine(Class<?> service, URL u, BufferedReader r, int lc,List<String> names)throws IOException, ServiceConfigurationError{
        String ln = r.readLine();
        if (ln == null) {
            return -1;
        }
        int ci = ln.indexOf('#');
        if (ci >= 0) ln = ln.substring(0, ci);
        ln = ln.trim();
        int n = ln.length();
        if (n != 0) {
            if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
                fail(service, u, lc, "Illegal configuration-file syntax");
            int cp = ln.codePointAt(0);
            if (!Character.isJavaIdentifierStart(cp))
                fail(service, u, lc, "Illegal provider-class name: " + ln);
            for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
                cp = ln.codePointAt(i);
                if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
                    fail(service, u, lc, "Illegal provider-class name: " + ln);
            }
            if (!providers.containsKey(ln) && !names.contains(ln))
                names.add(ln);
        }
        return lc + 1;
    }


    /**
     * 解析配置文件
     * */
    private Iterator<String> parse(Class<?> service, URL u)    throws ServiceConfigurationError  {
        System.out.println("INFO => 开始解析 配置文件URL==> " + u.getPath());
        InputStream in = null;
        BufferedReader r = null;
        ArrayList<String> names = new ArrayList<>();
        try {
            in = u.openStream();
            r = new BufferedReader(new InputStreamReader(in, "utf-8"));
            int lc = 1;
            while ((lc = parseLine(service, u, r, lc, names)) >= 0);
        } catch (IOException x) {
            fail(service, "Error reading configuration file", x);
        } finally {
            try {
                if (r != null) r.close();
                if (in != null) in.close();
            } catch (IOException y) {
                fail(service, "Error closing configuration file", y);
            }
        }
        return names.iterator();
    }


    /**
     * 懒加载器内部类;
     * */
    class LazyIterator implements Iterator<S>    {

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;

        private LazyIterator(Class<S> service, ClassLoader loader) {
            this.service = service;
            this.loader = loader;
        }

        private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                    String fullName = PREFIX + service.getName();
                    System.out.println(" INFO  fullName ==> " + fullName);
                    if (loader == null){

                        configs = ClassLoader.getSystemResources(fullName);
                    }
                    else{
                        System.out.println("  INFO  classloader 从  resource 路径加载了......");
                        configs = loader.getResources(fullName);
                    }
    /*              //注意 :   configs.nextElement(); 是一个指针,  只要调用这个方法,  那么它当前的对象就变了;
                    while (configs.hasMoreElements()) {
                        URL url = configs.nextElement();
                        System.out.println(  " INFO  路径是:   " +   url.getPath());
                        System.out.println(  " INFO  文件是:   " +   url.getFile());
                    }*/

                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }

        private S nextService() {
            if (!hasNextService()){throw new NoSuchElementException();}
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,"Provider " + cn + " not found");
            }
            if (!service.isAssignableFrom(c)) {
                fail(service, "Provider " + cn  + " not a subtype");
            }
            try {
                S p = service.cast(c.newInstance());
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                fail(service,"Provider " + cn + " could not be instantiated",x);
            }
            throw new Error();          // This cannot happen
        }

        public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }

    }



    public Iterator<S> iterator() {
        return new Iterator<S>() {

            Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();

            public boolean hasNext() {
                if (knownProviders.hasNext())
                    return true;
                return lookupIterator.hasNext();
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return (S) lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

    /**
     * Creates a new service loader for the given service type and class
     * loader.
     *
     * @param  <S> the class of the service type
     *
     * @param  service
     *         The interface or abstract class representing the service
     *
     * @param  loader
     *         The class loader to be used to load provider-configuration files
     *         and provider classes, or <tt>null</tt> if the system class
     *         loader (or, failing that, the bootstrap class loader) is to be
     *         used
     *
     * @return A new service loader
     */
    public static <S>   MyServiceLoader<S> load(Class<S> service, ClassLoader loader) {
        return new  MyServiceLoader<S>(service, loader);
    }


    public static <S>  MyServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        System.out.println("  INFO ==>  当前的加载器是 "  +  cl.toString() );
        return  MyServiceLoader.load(service, cl);
    }

    /**
     * Creates a new service loader for the given service type, using the
     * extension class loader.
     *
     * <p> This convenience method simply locates the extension class loader,
     * call it <tt><i>extClassLoader</i></tt>, and then returns
     *
     * <blockquote><pre>
     * MyServiceLoader.load(<i>service</i>, <i>extClassLoader</i>)</pre></blockquote>
     *
     * <p> If the extension class loader cannot be found then the system class
     * loader is used; if there is no system class loader then the bootstrap
     * class loader is used.
     *
     * <p> This method is intended for use when only installed providers are
     * desired.  The resulting service will only find and load providers that
     * have been installed into the current Java virtual machine; providers on
     * the application's class path will be ignored.
     *
     * @param  <S> the class of the service type
     *
     * @param  service
     *         The interface or abstract class representing the service
     *
     * @return A new service loader
     */
    public static <S> MyServiceLoader<S> loadInstalled(Class<S> service) {
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        ClassLoader prev = null;
        while (cl != null) {
            prev = cl;
            cl = cl.getParent();
        }
        return MyServiceLoader.load(service, prev);
    }


    public String toString() {
        return "MyServiceLoader[" + service.getName() + "]";
    }

}

